/*
 * @ : Copyright (c) 2021 Phytium Information Technology, Inc. 
 *  
 * SPDX-License-Identifier: Apache-2.0.
 * 
 * @Date: 2021-07-14 18:25:15
 * @LastEditTime: 2021-08-03 18:43:06
 * @Description:  This files is for 
 * 
 * @Modify History: 
 *  Ver   Who        Date         Changes
 * ----- ------     --------    --------------------------------------
 */
#include "ft_assert.h"
#include "kernel.h"
#include "fsleep.h"
#include "timer_tacho_hw.h"
#include "timer_tacho.h"

extern u32 TimerEnableIntr(TimerTachoCtrl *pCtrl);
extern void TimerDisableIntr(TimerTachoCtrl *pCtrl);
u32 TachoInit(TimerTachoCtrl *pCtrl, const TimerTachoConfig *pConfig)
{
    FT_ASSERTZERONUM(pCtrl && pConfig);

    u32 RegVal = 0;
    u32 Ret = TIMER_SUCCESS;

    pCtrl->IsReady = FALSE;

    TimerDisableIntr(pCtrl);

    /* set work mode */
    if (TIMER_WORK_MODE_TACHO == pConfig->WorkMode)
    {
        RegVal |= TIMER_REG_TACHO_MODE_TACHO;

        /* plus num of rpm calculate period */
        TIMER_CMPL_WRITE(pCtrl, pConfig->PlusNum);        
    }
    else if (TIMER_WORK_MODE_CAPTURE == pConfig->WorkMode)
    {
        RegVal |= TIMER_REG_TACHO_MODE_CAPTURE;
        
        /* set capture cnt to assert capture intr */
        RegVal |= TIMER_REG_TACHO_CAPTURE_CNT && 
                (pConfig->CaptueCnt << TIMER_REG_TACHO_CAPTURE_CNT_SHIFT);
    }
    else 
    {
        TIMER_ERROR("not support");
        return TIMER_INVALID_PARM;
    }
    
    /* set timer bits */
    if (TIMER_32_BITS == pConfig->TimerBits)
    {
        RegVal |= TIMER_REG_CNT_SERIES_32BIT;
    }
    else if (TIMER_64_BITS == pConfig->TimerBits)
    {
        TIMER_ERROR("not support");
        return TIMER_INVALID_PARM;
    }
    else
    {
        TIMER_ERROR("invalid input");
        return TIMER_INVALID_PARM;
    }

    /* set edge mode */
    if (TACHO_FALLING_EDGE == pConfig->EdgeMode)
    {
        RegVal |= TACHO_REG_MODE_FALLING_EDGE;
    }
    else if (TACHO_RISING_EDGE == pConfig->EdgeMode)
    {
        RegVal |= TACHO_REG_MODE_RISING_EDGE;
    }
    else if (TACHO_DOUBLE_EDGE == pConfig->EdgeMode)
    {
        RegVal |= TACHO_REG_MODE_DOUBLE_EDGE;
    }
    else
    {
        TIMER_ERROR("invalid input");
        return TIMER_INVALID_PARM;
    }    

    /* set jitter level */
    RegVal |= TACHO_REG_ANTI_JITTER_MASK & 
              (pConfig->Debounce << TACHO_REG_ANTI_JITTER_SHIFT);

    TIMER_CTRL_WRITE(pCtrl, RegVal);

    if (TIMER_SUCCESS == Ret)
    {
        //use input config
        if (pConfig != &pCtrl->Config)
        {
            pCtrl->Config = *pConfig;
        }

        pCtrl->IsReady = TRUE;
        TimerEnableIntr(pCtrl);   
    }

    return Ret;
}

u32 TachoGetFanRPM(TimerTachoCtrl *pCtrl)
{
    u32 loopCnt;
    u32 rawDat;
    u32 Rpm;
    u32 cntNum = TIMER_CMPL_READ(pCtrl);

    for (loopCnt = 0;; loopCnt++)
    {
        rawDat = TACHO_RESU_READ(pCtrl);
        /* wait for tacho result */
        if (rawDat & TACHO_REG_RESU_ISVALID)
        {
            break;
        }

        if (loopCnt > 3000)
        {
            fsleep_millisec(20); //20ms
        }
        else
        {
            fsleep_microsec(100);  //100ms          
        }
    }

    rawDat &= TACHO_REG_RESU_MASK; 
    if (0 == rawDat)
    {
        Rpm = 0;
    }
    else
    {
        // calculate rpm
        // (60 * freq * tacho) / (2 * (cmp_l + 1)) cmp_l       
        Rpm = (TIMER_CLK_FREQ_HZ * 60 * rawDat) / (2 * (cntNum + 1));
    }

    return Rpm;
}

u32 TachoGetCaptureCnt(TimerTachoCtrl *pCtrl)
{
    u32 capCnt;

    /* read cap cnt will clear it */
    capCnt = TACHO_REG_RESU_MASK & TACHO_RESU_READ(pCtrl);
    return capCnt;
}

u32 TimerSwithMode(TimerTachoCtrl *pCtrl, TimerTachoConfig *pNewConfig)
{
    FT_ASSERTZERONUM(pCtrl && pNewConfig);
    
    u32 Ret;
    
    if (pCtrl->Config.WorkMode == pNewConfig->WorkMode)
    {
        return TIMER_SUCCESS;
    }

    /* disable and clear timer */
    u32 RegVal = TIMER_CTRL_READ(pCtrl);
    RegVal &= (~TIMER_REG_ENABLE);
    RegVal |= TIMER_REG_CNT_CLR;
    TIMER_CTRL_WRITE(pCtrl, RegVal);

    if (TIMER_WORK_MODE_TIMER == pNewConfig->WorkMode)
    {
        Ret = TimerInit(pCtrl, pNewConfig);
    }
    else
    {
        Ret = TachoInit(pCtrl, pNewConfig);
    }

    return Ret;
}

void TachoSetCntPeriod(TimerTachoCtrl *pCtrl, u32 ticks)
{
    TIMER_CMPL_WRITE(pCtrl, ticks);
}

void TachoSetOverLimit(TimerTachoCtrl *pCtrl, u32 overLim)
{
    TACHO_OVER_WRITE(pCtrl, overLim);
}

void TachoSetUnderLimit(TimerTachoCtrl *pCtrl, u32 underLim)
{
    TACHO_UNDER_WRITE(pCtrl, underLim);
}